
`include "common_header.verilog"

//  *************************************************************************
//  File : rs_tx_64.v
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2014 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Muhammad Anisur Rahman, Thomas Schrobenhauser
//  info@morethanip.com
//  *************************************************************************
//  Description : 40G MAC Reconciliation Sub-Layer (Tx)
//  Version     : $Id: rs_tx_64.v,v 1.4 2014/10/23 09:40:50 ts Exp $
//  *************************************************************************

module rs_tx_64 (
   reset_txclk,
   xgmii_mode,
   force_send_idle,
   disable_flt_hdl,
   lowp_ena_t,
   xlgmii_txclk,
   xlgmii_txclk_ena,
   xlgmii_txc,
   xlgmii_txd,
   tx_val,
   tx_d,
   tx_final_size,
   tx_err,
   tx_end,
   tx_rsfault,
   lf_state,
   rf_state,
   li_state,
   tx_loc_fault,
   tx_rem_fault,
   tx_li_fault
   );

input   reset_txclk;            //  Async active high reset
input   xgmii_mode;             //  XGMII mode enable (0=XLGMII, 1=XGMII)
input   force_send_idle;        //  Force Idle send (async)
input   disable_flt_hdl;        //  if '1', RX RS does not affect TX data path
input   lowp_ena_t;             //  Low Power Generation (sync'ed to txclk)
input   xlgmii_txclk;           //  XLGMII Transmit Clock
input   xlgmii_txclk_ena;       //  XLGMII Transmit Clock Enable.
output  [7:0] xlgmii_txc;       //  XLGMII Transmit Control
output  [63:0] xlgmii_txd;      //  XLGMII Transmit Data
input   tx_val;                 //  Transmit data valid
input   [63:0] tx_d;            //  Transmit data
input   [2:0] tx_final_size;    //  Transmit final data word size
input   tx_err;                 //  Transmit error
input   tx_end;                 //  Transmit last word on tx_d (eop)
output  tx_rsfault;             //  Hold MAC TX as RS is in fault
input   lf_state;               //  Status from Receiver
input   rf_state;               //  Status from Receiver
input   li_state;               //  Status from Receiver
input   tx_loc_fault;           //  Force TX LF (txclk)
input   tx_rem_fault;           //  Force TX RF (txclk)
input   tx_li_fault;            //  Force TX LI (txclk)

reg     [7:0] xlgmii_txc;
reg     [63:0] xlgmii_txd;
wire    tx_rsfault;

parameter STM_TYP_IDLE  = 3'b 000;
parameter STM_TYP_START = 3'b 001;
parameter STM_TYP_DATA  = 3'b 010;
parameter STM_TYP_FEND  = 3'b 011;
parameter STM_TYP_END0  = 3'b 100;
parameter STM_TYP_FAULT = 3'b 101;

reg     [2:0] state;
reg     [2:0] nextstate;

wire    [8:0] lane_0;
wire    [8:0] lane_1;
wire    [8:0] lane_2;
wire    [8:0] lane_3;
wire    [8:0] lane_4;
wire    [8:0] lane_5;
wire    [8:0] lane_6;
wire    [8:0] lane_7;
wire    force_idle_s;           //  Synchronised on tx clock
wire    lf_state_s;             //  Synchronised on tx clock
wire    rf_state_s;             //  Synchronised on tx clock
wire    li_state_s;             //  Synchronised on tx clock
wire    xgmii_mode_s;           //  Synchronised on tx clock
wire    disable_flt_hdl_s;      //  Synchronised on tx clock
reg     send_idle_ltch;         //  latched send idle
reg     idle_pat_0;             //  Idle Pattern, bit0
reg     idle_state_p;           //  delay pipe for IDLE state for delaying lowp_ena
wire    [7:0] idle_pat;         //  Idle Pattern (0x07 or 0x06)
reg     send_idle;              //  send idle column
reg     send_fault;             //  send fault sequence
reg     [1:0] send_fault_type;  //  fault type last 2 bits


//  Clock domain crossing
//  ---------------------
mtip_xsync #(6) U_SYNC (
          .data_in({xgmii_mode, disable_flt_hdl, force_send_idle, lf_state, rf_state, li_state}),
          .reset(reset_txclk),
          .clk(xlgmii_txclk),
          .data_s({xgmii_mode_s, disable_flt_hdl_s, force_idle_s, lf_state_s, rf_state_s, li_state_s}));


//  Inform MAC that it should hold delivering further frames,
//  as RS is not accepting data from MAC currently.
//  Note: We do NOT hold the MAC when the user forces idle transmission,
//        as this is used to flush the TX.
//  --------------------------------------
assign tx_rsfault = send_fault |
                    rf_state_s & ~disable_flt_hdl_s |
                    li_state_s & ~disable_flt_hdl_s;


//  Determine fault/idle transmission
//  ---------------------------------
always @(posedge xlgmii_txclk or posedge reset_txclk)
   begin : i_tx
   if (reset_txclk == 1'b 1)
      begin
      send_idle      <= 1'b 0;
      send_idle_ltch <= 1'b 0;
      send_fault     <= 1'b 0;
      send_fault_type <= 2'b 00;
      end
   else
      begin
      //  determine SEQUENCE transmission
      //  -------------------------------
      send_fault <= (lf_state_s & ~disable_flt_hdl_s) | tx_loc_fault | tx_rem_fault |
                    (tx_li_fault & xgmii_mode_s);   // LI fault only valid in 10G/25G XGMII mode

      if (tx_loc_fault == 1'b 1 & tx_rem_fault == 1'b 0 & (tx_li_fault == 1'b 0 | xgmii_mode_s == 1'b 0))
         begin
         send_fault_type <= 2'b 01;   //  1: LF sequence
         end
      else if (tx_li_fault == 1'b 1 & xgmii_mode_s == 1'b 1)
         begin
         send_fault_type <= 2'b 11;   //  3: LinkInterruption fault
         end
      else
         begin
         //  if any other fault would assert, or tx_xxx_fault is ambiguous,
         //  it can only be RF (our TX must not be illegal, and a RF is never illegal)
         send_fault_type <= 2'b 10;   //  2: RF sequence
         end

      //  determine IDLE transmission
      //  ---------------------------
      send_idle <= rf_state_s & ~disable_flt_hdl_s | li_state_s & ~disable_flt_hdl_s | force_idle_s;

      //  avoid TX producing broken frame after IDLE/SEQUENCE condition disappears
      //  ------------------------------------------------------------------------
      //  CLOCK ENABLE
      if (xlgmii_txclk_ena == 1'b 1)
         begin
         //  latch fault until TX is idle
         if ((send_idle == 1'b 1 | send_fault == 1'b 1) & tx_val == 1'b 1)
            begin
            send_idle_ltch <= 1'b 1;   //  force IDLE until data valid deasserted
            end
         else if (tx_val == 1'b 0)
            begin
            send_idle_ltch <= 1'b 0;
            end
         end
      end
   end


//  State Machine
//  -------------
always @(posedge xlgmii_txclk or posedge reset_txclk)
   begin : i_stmr
   if (reset_txclk == 1'b 1)
      begin
      state <= STM_TYP_IDLE;
      end
   else
      begin
      //  CLOCK ENABLE
      if (xlgmii_txclk_ena == 1'b 1)
         begin
         state <= nextstate;
         end
      end
   end

always @(state or tx_val or send_fault or send_idle or send_idle_ltch or tx_final_size)
   begin : stm
   case (state)
   STM_TYP_IDLE:
      begin
      if (send_fault == 1'b 1)
         begin
         nextstate = STM_TYP_FAULT;
         end
      else if (tx_val == 1'b 1 & send_idle_ltch == 1'b 0 & send_idle == 1'b 0)
         begin
         nextstate = STM_TYP_START;
         end
      else
         begin
         nextstate = STM_TYP_IDLE;
         end
      end

   STM_TYP_START:
      begin
      nextstate = STM_TYP_DATA;   //  send at least two columns of data (fault and forced idle has no effect)
      end

   STM_TYP_DATA:
      begin
      if (tx_val == 1'b 0)
         begin
         nextstate = STM_TYP_END0;   //  TERM in lane0
         end
      else if (tx_val == 1'b 1 & tx_final_size != 3'b 000)
         begin
         nextstate = STM_TYP_FEND;   //  TERM in lane other than lane0
         end
      else if (send_fault == 1'b 1)
         begin
         //  force to remote fault for local fault condition
         nextstate = STM_TYP_FAULT;
         end
      else if (send_idle == 1'b 1)
         begin
         nextstate = STM_TYP_IDLE;   //  force to idle for remote fault condition
         end
      else
         begin
         nextstate = STM_TYP_DATA;
         end
      end

   STM_TYP_FEND:
      begin
      if (send_fault == 1'b 1)
         begin
         nextstate = STM_TYP_FAULT;
         end
      else if (tx_val == 1'b 1 & send_idle_ltch == 1'b 0 & send_idle == 1'b 0)
         begin
         nextstate = STM_TYP_START;
         end
      else
         begin
         nextstate = STM_TYP_IDLE;
         end
      end

   STM_TYP_END0:
      begin
      if (send_fault == 1'b 1)
         begin
         nextstate = STM_TYP_FAULT;
         end
      else if (tx_val == 1'b 1 & send_idle_ltch == 1'b 0 & send_idle == 1'b 0)
         begin
         nextstate = STM_TYP_START;
         end
      else
         begin
         nextstate = STM_TYP_IDLE;
         end
      end

   STM_TYP_FAULT:
      begin
      if (send_fault == 1'b 0)
         begin
         //  return only when no transmission (no data and no remote fault) is ongoing
         nextstate = STM_TYP_IDLE;
         end
      else
         begin
         nextstate = STM_TYP_FAULT;
         end
      end

   default:
      begin
      nextstate = STM_TYP_IDLE;
      end

   endcase
   end


//  Select Idle Pattern
//  -------------------
always @(posedge xlgmii_txclk or posedge reset_txclk)
   begin : ip0p
   if (reset_txclk == 1'b 1)
      begin
      idle_pat_0 <= 1'b 1;
      idle_state_p <= 1'b 0;
      end
   else
      begin
      //  Assert LPI only after minimum of 3 columns of idle have been transmitted following a frame.
      if (state == STM_TYP_IDLE)
         begin
         idle_state_p <= 1'b 1;
         end
      else
         begin
         idle_state_p <= 1'b 0;
         end

      //  Ensure reaction on LinkInterruption and forced idle is a 0x07 idle and never a LPI
      //  LPI only if nothing else overwrites it.
      idle_pat_0 <= li_state_s | force_idle_s | ~lowp_ena_t | ~idle_state_p;
      end
   end

assign idle_pat = {7'b 0000011, idle_pat_0};   // 6(LPI) or 7(normal)


//  Data MUX
//  --------
assign lane_0 = nextstate == STM_TYP_END0  ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_START ? {1'b 1, 8'h FB} :
                nextstate == STM_TYP_FAULT ? {1'b 1, 8'h 9C} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                `ifdef MTIPM40_RSTX_INS_FE
                // Avoid PCS encoding issue when next frame starts, if corrupting at end of frame in same column as TERM.
                // The MAC already corrupts CRC hence this frame is already in error and no special treatment in RS is needed.
                tx_err == 1'b 1 & tx_end == 1'b 1 ? {1'b 1, 8'h FE} :
                `endif
                {1'b 0, tx_d[7:0]};

assign lane_1 = nextstate == STM_TYP_FEND & tx_final_size == 3'b 001 ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_END0  ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                nextstate == STM_TYP_FAULT ? {1'b 0, 8'h 00} : {1'b 0, tx_d[15:8]};

assign lane_2 = nextstate == STM_TYP_FEND & tx_final_size == 3'b 010 ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_FEND & tx_final_size <  3'b 010 ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_END0  ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                nextstate == STM_TYP_FAULT ? {1'b 0, 8'h 00} : {1'b 0, tx_d[23:16]};

assign lane_3 = nextstate == STM_TYP_FEND & tx_final_size == 3'b 011 ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_FEND & tx_final_size <  3'b 011 ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_END0  ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                nextstate == STM_TYP_FAULT ? {1'b 0, 6'b 000000, send_fault_type} :
                {1'b 0, tx_d[31:24]};

assign lane_4 = nextstate == STM_TYP_FEND & tx_final_size == 3'b 100 ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_FEND & tx_final_size <  3'b 100 ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_END0  ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                nextstate == STM_TYP_FAULT & xgmii_mode_s == 1'b 0 ? {1'b 0, 8'h 00} :
                nextstate == STM_TYP_FAULT & xgmii_mode_s == 1'b 1 ? {1'b 1, 8'h 9C} :
                {1'b 0, tx_d[39:32]};

assign lane_5 = nextstate == STM_TYP_FEND & tx_final_size == 3'b 101 ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_FEND & tx_final_size <  3'b 101 ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_END0  ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                nextstate == STM_TYP_FAULT ? {1'b 0, 8'h 00} : {1'b 0, tx_d[47:40]};

assign lane_6 = nextstate == STM_TYP_FEND & tx_final_size == 3'b 110 ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_FEND & tx_final_size <  3'b 110 ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_END0  ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                nextstate == STM_TYP_FAULT ? {1'b 0, 8'h 00} : {1'b 0, tx_d[55:48]};

assign lane_7 = nextstate == STM_TYP_FEND & tx_final_size == 3'b 111 ? {1'b 1, 8'h FD} :
                nextstate == STM_TYP_FEND & tx_final_size <  3'b 111 ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_END0  ? {1'b 1, 8'h 07} :
                nextstate == STM_TYP_IDLE  ? {1'b 1, idle_pat} :
                nextstate == STM_TYP_FAULT & xgmii_mode_s == 1'b 0 ? {1'b 0, 8'h 00} :
                nextstate == STM_TYP_FAULT & xgmii_mode_s == 1'b 1 ? {1'b 0, 6'b 000000, send_fault_type} :
                {1'b 0, tx_d[63:56]};


//  Output register
//  ---------------
always @(posedge xlgmii_txclk or posedge reset_txclk)
   begin : outreg
   if (reset_txclk == 1'b 1)
      begin
      xlgmii_txd <= {8{8'h 07}};   //  XLGMII transmit data
      xlgmii_txc <= {8{1'b 1}};    //  XLGMII transmit control
      end
   else
      begin
      //  CLOCK ENABLE
      if (xlgmii_txclk_ena == 1'b 1)
         begin
         xlgmii_txd <= {lane_7[7:0], lane_6[7:0], lane_5[7:0], lane_4[7:0],
                        lane_3[7:0], lane_2[7:0], lane_1[7:0], lane_0[7:0]};
         xlgmii_txc <= {lane_7[8], lane_6[8], lane_5[8], lane_4[8],
                        lane_3[8], lane_2[8], lane_1[8], lane_0[8]};
         end
      end
   end


endmodule // module rs_tx_64
